#ifndef SENTRY_REPLAY_ENCODER_H
#define SENTRY_REPLAY_ENCODER_H

#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include <native_window/external_window.h>

struct CodecContext;
struct OH_AVBuffer;
struct OH_AVMuxer;
struct OH_AVCodec;
struct OH_AVFormat;
struct NativePixelMap;

class ReplayEncoder {
public:
    static ReplayEncoder &Instance();

    bool Init(int32_t fd, int32_t width, int32_t height, int32_t fps, const std::string &mimeType);
    bool Encode(const void *rgba, size_t size, int64_t timestampMs, int32_t inputStride = 0, int32_t inputWidth = 0, int32_t inputHeight = 0);
    // Use void* to avoid NativePixelMap type definition issues across TUs
    bool EncodePixelMap(void *pixelMap, int64_t timestampMs);
    
    // Returns the input surface (OHNativeWindow*) for the encoder.
    // Caller does NOT own the returned pointer (it belongs to the encoder).
    void* GetInputWindow();
    
    void Finalize();

private:
    ReplayEncoder();
    ~ReplayEncoder();
    ReplayEncoder(const ReplayEncoder &) = delete;
    ReplayEncoder &operator=(const ReplayEncoder &) = delete;

    struct CodecContext;

    void ResetState();
    void DrainOutputLoop();
    void ApplyBackgroundPriority();
    bool ConvertRgbaToNv12(const uint8_t *rgba, size_t size, uint8_t *dest, int32_t inputStride, int32_t inputWidth, int32_t inputHeight);
    void HandleCodecError(int32_t errorCode);
    void HandleFormatChange();

    static void OnCodecError(struct OH_AVCodec *codec, int32_t errorCode, void *userData);
    static void OnCodecFormatChange(struct OH_AVCodec *codec, struct OH_AVFormat *format, void *userData);
    static void OnNeedInputBuffer(struct OH_AVCodec *codec, uint32_t index, struct OH_AVBuffer *buffer, void *userData);
    static void OnNewOutputBuffer(struct OH_AVCodec *codec, uint32_t index, struct OH_AVBuffer *buffer, void *userData);

private:
    bool ready_ = false;
    int32_t duplicatedFd_ = -1;
    int32_t width_ = 0;
    int32_t height_ = 0;
    int32_t fps_ = 0;
    std::string mimeType_;

    OH_AVMuxer *muxer_ = nullptr;
    int32_t videoTrackId_ = -1;
    bool muxerStarted_ = false;

    OH_AVCodec *videoEncoder_ = nullptr;
    bool encoderStarted_ = false;
    OHNativeWindow *nativeWindow_ = nullptr;

    std::unique_ptr<CodecContext> context_;
    std::thread drainThread_;
    std::atomic<bool> stopRequested_ {false};

    std::vector<uint8_t> nv12Buffer_;
    size_t expectedFrameSize_ = 0;
    uint32_t yStride_ = 0;
    uint32_t uvStride_ = 0;
    size_t yPlaneSize_ = 0;
    size_t uvPlaneSize_ = 0;
    bool firstFrame_ = true;
    uint64_t frameCount_ = 0;
    uint64_t writtenSamples_ = 0;
    int64_t lastPtsUs_ = 0;
    int64_t basePtsUs_ = 0;
    bool hasBasePts_ = false;
    int64_t frameDurationUs_ = 0;
};

#endif // SENTRY_REPLAY_ENCODER_H

